package com.lcp.se.service;

import com.lcp.se.entity.result.Result;
import com.lcp.se.entity.vo.UploadFileInfo;
import com.lcp.se.util.ProjectConstants;
import io.minio.*;
import io.minio.errors.*;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;

/**
 * @author lcp
 * @date 2020/7/20 21:45
 */
@Service
@ConfigurationProperties(prefix = "minio")
public class MinioService {
    private final Logger log = LoggerFactory.getLogger(MinioService.class);
    // System.getProperty("user.dir") 返回的为当前项目的工作目录，即在哪个地方启动的java线程
    private final String dirPath = System.getProperty("user.dir") + File.separator + "tmp" + File.separator;
    @Resource
    private MinioClient minioClient;
    private List<String> buckets;

    // 使用了 @ConfigurationProperties 需要对属性写 setter 方法
    public void setBuckets(List<String> buckets) {
        this.buckets = buckets;
    }

    @PostConstruct
    public void init() {
        createBucketIfNotExists();
        createDirIfNotExists();
    }

    /**
     * 检查缓存文件夹是否存在，不存在则创建
     */
    private void createDirIfNotExists() {
        File file = new File(dirPath);
        while (!file.exists()) {
            boolean b = file.mkdirs();
            if (b) return;
        }
    }

    /**
     * 检查相关 bucket 是否存在，不存在则创建
     */
    public void createBucketIfNotExists() {
        try {
            for (String bucketName : buckets) {
                log.info("检查bucket：" + bucketName + " 是否存在");
                boolean b = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
                if (!b) {
                    log.info("bucket：" + bucketName + " 不存在，尝试创建名为：" + bucketName + " 的bucket中");
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                    log.info("bucket：" + bucketName + " 创建成功");
                } else {
                    log.info("bucket：" + bucketName + " 存在");
                }
            }
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException
                | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException
                | ServerException | XmlParserException | RegionConflictException e) {
            e.printStackTrace();
            log.info("创建bucket出错：" + e.getMessage());
        }
    }

    /**
     * 上传文件并重命名、转码
     */
    public Result<UploadFileInfo> upload(MultipartFile file, String openId, String bucketName) {
        if (!buckets.contains(bucketName)) {
            return Result.failure();
        }
        Process process = null;
        File uploadFile = null;
        File tmpFile = null;
        try {
            // 使用 openId + SEPARATOR_FILE + 毫秒时间戳 来重命名文件
            String orgFileName = file.getOriginalFilename();
            if (orgFileName == null) {
                return Result.failure("上传失败");
            }
            String fileName = openId + ProjectConstants.SEPARATOR_FILE
                    + System.currentTimeMillis() + orgFileName.substring(orgFileName.lastIndexOf("."));
            log.info("接收到文件：" + fileName);

            String tmpFileName = openId + ProjectConstants.SEPARATOR_FILE
                    + System.currentTimeMillis() + "_out" + orgFileName.substring(orgFileName.lastIndexOf("."));

            // 上传的文件
            uploadFile = new File(dirPath + fileName);
            // 转码后的临时文件
            tmpFile = new File(dirPath + tmpFileName);

            // 将上传的文件存到本地
            file.transferTo(uploadFile);

            // 拼接转码命令，添加 -ab 48k 可以不压缩文件
            String cmd = "ffmpeg -i " + uploadFile.getAbsolutePath() + " " + tmpFile.getAbsolutePath();
            log.info("执行转码命令：" + cmd);
            // 执行转码命令
            process = Runtime.getRuntime().exec(cmd);
            // 等待执行完成并获取 exitValue
            int i = process.waitFor();

            // 判断是否转码成功，0 为成功
            if (i == 0) {
                log.info("转码成功");
                String contentType = file.getContentType();
                // 将转码后的本地文件上传到 minio
                minioClient.uploadObject(
                        UploadObjectArgs.builder()
                                .bucket(bucketName)
                                .object(fileName)
                                .filename(tmpFile.getAbsolutePath())
                                .contentType(contentType)
                                .build());
                return Result.success("上传成功", new UploadFileInfo(fileName));
            } else {
                log.info("转码失败");
                return Result.failure("上传失败");
            }
        } catch (Exception e) {
            log.info("上传文件出错：" + e.getMessage());
        } finally {
            if (process != null) process.destroy();
            if (uploadFile != null) uploadFile.delete();
            if (tmpFile != null) tmpFile.delete();
        }
        return Result.failure("上传失败");
    }

    /**
     * 下载文件
     */
    public void download(HttpServletResponse response, String fileName, String bucketName) {
        if (!buckets.contains(bucketName)) {
            return;
        }
        InputStream in = null;
        try {
            //获取文件对象 stat原信息
            ObjectStat stat = minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
            response.setContentType(stat.contentType());
            //response.setContentType("application/octet-stream; charset=UTF-8");
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 删除操作
     */
    public Result<Object> delete(String fileName, String bucketName) {
        if (!buckets.contains(bucketName)) {
            return Result.failure();
        }
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
            return Result.success("删除成功");
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException
                | InvalidKeyException | InvalidResponseException | IOException
                | NoSuchAlgorithmException | ServerException | XmlParserException e) {
            e.printStackTrace();
            return Result.failure("删除失败");
        }
    }
}
